package diskwatch

import (
	"context"
	"os"
	"sort"
	"strings"
	"time"

	"gitee.com/smewl13/beehive/pkg/config"
	"github.com/shirou/gopsutil/v3/disk"
	"github.com/spf13/viper"
	"go.uber.org/zap"
)

type FileInfo struct {
	ModTime time.Time
	Path    string
}

type Cache []*FileInfo

func (c Cache) Len() int {
	return len(c)
}

func (c Cache) Less(i, j int) bool {
	t1 := c[i].ModTime.Unix()
	t2 := c[j].ModTime.Unix()
	return t1 < t2
}

func (c Cache) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}

func NewPartitionMonitor(logger *zap.Logger, conf *viper.Viper) *PartitionMonitor {
	interval := strings.TrimSpace(conf.GetString("watch.disk.interval"))
	used := conf.GetFloat64("watch.disk.used")
	delOnce := conf.GetInt("watch.disk.delete_once")

	dur, err := config.ParseDuration(interval)
	if err != nil {
		logger.Fatal(
			"[初始化][磁盘守护][加载配置]磁盘巡检周期解析失败",
			zap.String("interval", interval),
			zap.Error(err),
		)
	}
	if dur == 0 {
		dur = config.Duration(time.Minute) * 10
	}

	if delOnce == 0 {
		delOnce = 1
	}

	if used == 0 {
		used = 80
	}

	return &PartitionMonitor{
		interval:    time.Duration(dur),
		generalUsed: used,
		onceNum:     delOnce,
		cache:       make(Cache, 0),
		delCH:       make(chan string, delOnce),
		logger:      logger,
	}
}

type PartitionMonitor struct {
	monutPoint  string
	interval    time.Duration
	generalUsed float64
	used        float64
	onceNum     int
	paths       []*PathMonitor
	cache       Cache
	delCH       chan string
	logger      *zap.Logger
}

func (pm *PartitionMonitor) Handle(ctx context.Context) {

	go pm.deleteFile()

	for {
		pm.monitor()
		time.Sleep(pm.interval)
	}
}

func (pm *PartitionMonitor) monitor() {
	pm.cache = make(Cache, 0)
	for _, p := range pm.paths {
		pm.cache = append(pm.cache, p.Handle(pm.delCH)...)
	}
	sort.Sort(pm.cache)
	pm.usedCheck()
}

func (pm *PartitionMonitor) usedCheck() {
	for {
		if len(pm.cache) == 0 {
			break
		}
		stat, err := disk.Usage(pm.monutPoint)
		if err != nil {
			pm.logger.Error(
				"[磁盘守护]分区使用信息失败",
				zap.String("mount_point", pm.monutPoint),
				zap.Error(err),
			)
			return
		}

		if stat.InodesUsedPercent < pm.used && stat.UsedPercent < pm.used {
			break
		}
		pm.logger.Info(
			"[磁盘守护]分区使用率大于阈值",
			zap.Float64("阈值(%)", pm.used),
			zap.Float64("size_used(%)", stat.UsedPercent),
			zap.Float64("inode_used(%)", stat.InodesUsedPercent),
		)
		items := pm.cache
		if len(pm.cache) > pm.onceNum {
			items = pm.cache[:pm.onceNum]
		}
		for _, item := range items {
			pm.delCH <- item.Path
		}
		pm.cache = pm.cache[len(items):]
	}
}

func (pm *PartitionMonitor) deleteFile() {
	for {
		file := <-pm.delCH
		if err := os.Remove(file); err != nil {
			pm.logger.Error(
				"[磁盘守护]删除文件失败",
				zap.String("mount_point", pm.monutPoint),
				zap.String("file", file),
				zap.Error(err),
			)
			continue
		}
		pm.logger.Info(
			"[磁盘守护]删除文件",
			zap.String("mount_point", pm.monutPoint),
			zap.String("file", file),
		)
	}
}
